package com.riekoff.particlecubeworld.editor.visual;

import java.util.ArrayList;

import cc.creativecomputing.CCApp;
import cc.creativecomputing.control.CCControl;
import cc.creativecomputing.graphics.CCGraphics;
import cc.creativecomputing.math.CCMath;
import cc.creativecomputing.math.CCVector2f;
import cc.creativecomputing.math.CCVector3f;
import cc.creativecomputing.math.random.CCRandom;
import cc.creativecomputing.math.signal.CCPerlinNoise;

import com.riekoff.particlecubeworld.player.ATileWorldBox;

import de.negativrekord.blockworld.MultiPathGenerator;
import de.negativrekord.blockworld.MultiPathGenerator.StartPositions;
import de.negativrekord.blockworld.camera.CameraSet;


//TODO: HOEHE QUANTISIERT
public class ABoxVisualFinal implements AVisual{

	private final int nDetailLevels = 3;
	private ArrayList<ATileWorldBox>[] _myBoxes = new ArrayList[nDetailLevels];
	private MultiPathGenerator _myPathGenerator = new MultiPathGenerator (10, StartPositions.GRID, 1,0,0,0);
	
	@CCControl(name="noise")
	public CCPerlinNoise _myNoise;
	
	private CCApp _myApp;
	private CCRandom _myRandom = new CCRandom();
	
	@CCControl(name="recalc")
	private boolean _cRecalc = false;
	
	@CCControl(name="seed", min = 0, max = 100)
	private int _cRandomSeed = 0;
	
	//@CCControl(name="max height", min = 0, max = 500)
	private int _cMaxHeight = 500;
	
	@CCControl(name="height raster", min = 0, max = 50)
	private int _cHeightRaster = 0;
	
	@CCControl(name="cam", tabName="cameras")
	private CameraSet _myCameraSet;
	
	private int NPATTERN = 10;
	private int NZONES = 10;
	private int NCOLORS = 10;
	
	private CCVector3f[] centersPattern = new CCVector3f[NPATTERN];
	private CCVector3f[] centersZone = new CCVector3f[NZONES];
	private CCVector3f[] centersColor = new CCVector3f[NCOLORS];
	
	int N = 40;
	public int size = 1000;
	public int planes[][][] = new int[6][N][N];
	
	
	
	
	public ABoxVisualFinal(CCApp theApp) {

		_myNoise = new CCPerlinNoise();
		_myApp = theApp;
		
		for (int i=0; i<nDetailLevels; i++) {
			_myBoxes[i] = new ArrayList<>();
		}
		
		for (int i=0; i<10; i++) {
			centersPattern[i] = new CCVector3f(1f,0f,1f);
			centersZone[i] = new CCVector3f(1f,0f,1f);
			centersColor[i] = new CCVector3f(1f,0f,1f);
		}
		
		recalc();
		_myCameraSet = new CameraSet(theApp, this);
	}
	
	public CCVector3f[]  centersPattern() {
		return centersPattern;
	}
	
	public CCVector3f[]  centersZone() {
		return centersZone;
	}
	
	public CCVector3f[]  centersColor() {
		return centersColor;
	}
	
	int minScale = 10;
	
	private void addBoxNoScale (CCVector3f pos, CCVector3f size) {
		size.add(new CCVector3f(-0.01f,-0.01f,-0.01f));
		_myBoxes[0].add (new ATileWorldBox (
			new CCVector3f(size.x,size.y,size.z), 
			new CCVector3f(pos.x,pos.y,pos.z)
		));
		
	}
	int getIndex (CCVector3f norm) {
		int k = 0;
		if (norm.x== 1) k = 0;
		if (norm.x==-1) k = 1;
		if (norm.y== 1) k = 2;
		if (norm.y==-1) k = 3;
		if (norm.z== 1) k = 4;
		if (norm.z==-1) k = 5;
		return k;
	}
	
	public void addPlane (int sizeA, int sizeB, int size, int height, CCVector3f norm) {
			
		float scale = size/N;
		for (int i=-N/2; i<N/2; i+=sizeA) {
			for (int j=-N/2; j<N/2; j+=sizeB) {
				
				int ii = (int)_myRandom.random(0, 100);
				int jj = (int)_myRandom.random(0, 100);
				
				float hh = CCMath.abs(_myNoise.value(i+N/2,j+N/2));
				float h = CCMath.abs(_myNoise.value(new CCVector2f(ii+i+N/2,jj+j+N/2))) * height;
				h = CCMath.floor(h / _cHeightRaster) * _cHeightRaster;
				int k = getIndex(norm);
				
				
//				int off = planes[k][i+N/2][j+N/2];
				for (int a=0; a<sizeA; a++) {
					for (int b=0; b<sizeB; b++) {	
						int aa = CCMath.constrain(i+N/2+a,0,N-1);
						int bb = CCMath.constrain(j+N/2+b,0,N-1);
						planes[k][aa][bb] = (int)(h) + size/2;		
					}
				}
				
				addBoxNoScale(getPos(norm, i, j, scale), getSize(norm, sizeA * scale, sizeB * scale, h));
			}
		}
	}
	
	public CCVector3f getPos (CCVector3f norm, int i, int j, float scale) {
		if (CCMath.abs(norm.x)==1) {
			return new CCVector3f(norm.x*N*scale/2, i*scale, j*scale);
		}
		if (CCMath.abs(norm.y)==1) {
			return new CCVector3f(i*scale, norm.y*N*scale/2, j*scale);
		}
		if (CCMath.abs(norm.z)==1) {
			return new CCVector3f(i*scale, j*scale, norm.z*N*scale/2);
		}
		return new CCVector3f();
	}
	
	public CCVector3f getSize (CCVector3f norm, float sizeA, float sizeB, float h) {
		if (CCMath.abs(norm.x)==1) {
			return new CCVector3f (norm.x*h, sizeA, sizeB);
		}
		if (CCMath.abs(norm.y)==1) {
			return new CCVector3f (sizeA, norm.y*h, sizeB);
		}
		if (CCMath.abs(norm.z)==1) {
			return new CCVector3f (sizeA, sizeB, norm.z*h);
		}
		return new CCVector3f();
	}
	
	void addPlaneDetail (int sizeA, int sizeB, int scale, int o, CCVector3f norm ) {
		
		int h = 2;
		for (int i=-N/2; i<N/2; i+=sizeA) {
			for (int j=-N/2; j<N/2; j+=sizeB) {
				int offset = planes[getIndex(norm)][i+N/2][j+N/2];
				
				if (_myRandom.random()>0.05) continue;
				int k = getIndex(norm);
				for (int a=0; a<sizeA; a++) {
					for (int b=0; b<sizeB; b++) {	
						int aa = CCMath.constrain(i+N/2+a,0,N-1);
						int bb = CCMath.constrain(j+N/2+b,0,N-1);
						planes[k][aa][bb] += (int)(h);		
					}
				}
				
				addBoxNoScale(getPos(norm, i,j,scale), getSize(norm, sizeA*scale, sizeB*scale, h));
				
			}
		}
	}
	
	public void recalc() {
		_myRandom.randomSeed(_cRandomSeed);
		_myBoxes[0].clear();
		planes = new int[6][N][N];
		
		addPlane(6,6, size, _cMaxHeight, new CCVector3f ( 1,0,0));
		addPlane(6,6, size, _cMaxHeight, new CCVector3f (-1,0,0));
		addPlane(6,6, size, _cMaxHeight, new CCVector3f (0, 1,0));
		addPlane(6,6, size, _cMaxHeight, new CCVector3f (0,-1,0));
		
//		addPlane(10,10,5, N, new CCVector3f(-1,0,0));
//		
//		addPlane(10,10,5, N-10, new CCVector3f(0, 1,0));
//		addPlane(10,10,5, N-5, new CCVector3f(0,-1,0));
//		
//		addPlane(10,10,5, N-10, new CCVector3f(0,0,1));
//		addPlane(10,10,5, N-5, new CCVector3f(0,0,-1));
		
		
//		addPlaneDetail(1,6,size,  20, new CCVector3f(1,0,0));
//		addPlaneDetail(4,1,5,   N, new CCVector3f(-1,0,0));
//		
//		addPlaneDetail(1,4,5,   N, new CCVector3f(0, 1,0));
//		addPlaneDetail(4,1,5,   N, new CCVector3f(0,-1,0));
//		
//		addPlaneDetail(1,4,5,   N, new CCVector3f(0,0, 1));
//		addPlaneDetail(4,1,5,   N, new CCVector3f(0,0,-1));
		
	}
	int cnt = 0;

	public CameraSet cameraSet(){
		return _myCameraSet;
	}
	
	ATileWorldBox getRandomBox (int theLevel) {
		int ind = (int)_myRandom.random(0,_myBoxes[theLevel].size());
		return _myBoxes[theLevel].get(ind);
	}
	
	CCVector3f[] _myCenters = new CCVector3f[10];
	public CCVector3f[] centers() {
		for (int i=0; i<10; i++) {
			_myCenters[i] = _myPathGenerator.spline(i);
		}	
		return _myCenters;
	}
	
	void addBoxDetail(int theLevel, int size0, int size1, int minH, int maxH) {

		ArrayList<ATileWorldBox> boxes = _myBoxes[theLevel];
		for (int i=0; i<size0/size1; i++) {
			for (int j=0; j<size0/size1; j++) {
				boxes.add(new ATileWorldBox (new CCVector3f(_myRandom.random(minH,maxH), size1, size1), new CCVector3f(size0/2,i*size1-size0/2,j*size1-size0/2)));
				boxes.add(new ATileWorldBox (new CCVector3f(_myRandom.random(minH,maxH), size1, size1), new CCVector3f(-size0/2,i*size1-size0/2,j*size1-size0/2)));
			}	
		}
		
		for (int i=0; i<size0/size1; i++) {
			for (int j=0; j<size0/size1; j++) {
				boxes.add(new ATileWorldBox (new CCVector3f(size1, _myRandom.random(minH,maxH), size1), new CCVector3f(-size0/2+i*size1,-size0/2, -size0/2+j*size1)));
				boxes.add(new ATileWorldBox (new CCVector3f(size1, _myRandom.random(minH,maxH), size1), new CCVector3f(-size0/2+i*size1, size0/2, -size0/2+j*size1)));
			}	
		}
		
		for (int i=0; i<size0/size1; i++) {
			for (int j=0; j<size0/size1; j++) {
				boxes.add(new ATileWorldBox (new CCVector3f(size1, size1, _myRandom.random(minH,maxH)), new CCVector3f(-size0/2+i*size1,-size0/2+j*size1,-size0/2)));
				boxes.add(new ATileWorldBox (new CCVector3f(size1, size1, _myRandom.random(minH,maxH)), new CCVector3f(-size0/2+i*size1,-size0/2+j*size1,size0/2)));
			}	
		}
		
		for (int i=0; i<100; i++) {
			int size = (int)_myRandom.random(10,40);
			boxes.add(new ATileWorldBox (new CCVector3f(size,size,size), new CCVector3f(_myRandom.random(-200,200),_myRandom.random(-200,200),_myRandom.random(-200,200))));
		}
	}
	
	public void update (float theDeltaTime) {
		if(_cRecalc) {
			recalc();
			_myCameraSet.recalc(this);	
		}
		_myPathGenerator.update(theDeltaTime);
		
		for (int i=0; i<10; i++) {
			centersColor[i] = _myPathGenerator.spline(i+4);
		}
		_myCameraSet.update(theDeltaTime);
	}

	
	public CCVector3f path(int i) {
		return _myPathGenerator.spline(0);
	}
	
	public void render (CCGraphics g) {
		for (int i=0; i<3; i++) {
			for (ATileWorldBox b: _myBoxes[i]) {
				b.draw(g);
			}
		}
	}
	
	@Override
	public void draw(CCGraphics g) {
		_myCameraSet.draw();
	}
}